package com.autumn.mybatis.mapper;

import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.OrderBy;
import javax.persistence.SequenceGenerator;

import org.apache.ibatis.type.BigDecimalTypeHandler;
import org.apache.ibatis.type.BigIntegerTypeHandler;
import org.apache.ibatis.type.BooleanTypeHandler;
import org.apache.ibatis.type.ByteArrayTypeHandler;
import org.apache.ibatis.type.ByteTypeHandler;
import org.apache.ibatis.type.CharacterTypeHandler;
import org.apache.ibatis.type.DateTypeHandler;
import org.apache.ibatis.type.DoubleTypeHandler;
import org.apache.ibatis.type.FloatTypeHandler;
import org.apache.ibatis.type.IntegerTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.LongTypeHandler;
import org.apache.ibatis.type.ShortTypeHandler;
import org.apache.ibatis.type.SqlDateTypeHandler;
import org.apache.ibatis.type.SqlTimeTypeHandler;
import org.apache.ibatis.type.SqlTimestampTypeHandler;
import org.apache.ibatis.type.StringTypeHandler;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.UnknownTypeHandler;

import com.autumn.mybatis.mapper.annotation.ColumnType;
import com.autumn.mybatis.type.TimeSpanTypeHandler;
import com.autumn.mybatis.type.TimeTypeHandler;
import com.autumn.util.Time;
import com.autumn.util.TimeSpan;
import com.autumn.util.reflect.BeanProperty;
import com.autumn.util.tuple.TupleTwo;

/**
 * 属性映射帮助
 * 
 * @author 杨昌国
 *
 *         2017-10-11 12:18:51
 */
public  class PropertyMapperUtils {
	private static Map<Class<?>, TupleTwo<JdbcType, Class<? extends TypeHandler<?>>>> jdbcTypeMap = new HashMap<>();

	static {

		TupleTwo<JdbcType, Class<? extends TypeHandler<?>>> tuple;

		tuple = new TupleTwo<>(JdbcType.VARCHAR, StringTypeHandler.class);
		jdbcTypeMap.put(String.class, tuple);

		tuple = new TupleTwo<>(JdbcType.VARCHAR, TimeTypeHandler.class);
		jdbcTypeMap.put(Time.class, tuple);

		tuple = new TupleTwo<>(JdbcType.VARCHAR, SqlTimeTypeHandler.class);
		jdbcTypeMap.put(java.sql.Time.class, tuple);

		tuple = new TupleTwo<>(JdbcType.BIGINT, TimeSpanTypeHandler.class);
		jdbcTypeMap.put(TimeSpan.class, tuple);

		tuple = new TupleTwo<>(JdbcType.TINYINT, ByteTypeHandler.class);
		jdbcTypeMap.put(Byte.class, tuple);
		jdbcTypeMap.put(Byte.TYPE, tuple);

		tuple = new TupleTwo<>(JdbcType.SMALLINT, ShortTypeHandler.class);
		jdbcTypeMap.put(Short.class, tuple);
		jdbcTypeMap.put(Short.TYPE, tuple);

		tuple = new TupleTwo<>(JdbcType.NCHAR, CharacterTypeHandler.class);
		jdbcTypeMap.put(Character.class, tuple);
		jdbcTypeMap.put(Character.TYPE, tuple);

		tuple = new TupleTwo<>(JdbcType.INTEGER, IntegerTypeHandler.class);
		jdbcTypeMap.put(Integer.class, tuple);
		jdbcTypeMap.put(Integer.TYPE, tuple);

		tuple = new TupleTwo<>(JdbcType.BIGINT, LongTypeHandler.class);
		jdbcTypeMap.put(Long.class, tuple);
		jdbcTypeMap.put(Long.TYPE, tuple);

		tuple = new TupleTwo<>(JdbcType.FLOAT, FloatTypeHandler.class);
		jdbcTypeMap.put(Float.class, tuple);
		jdbcTypeMap.put(Float.TYPE, tuple);

		tuple = new TupleTwo<>(JdbcType.DOUBLE, DoubleTypeHandler.class);
		jdbcTypeMap.put(Double.class, tuple);
		jdbcTypeMap.put(Double.TYPE, tuple);

		tuple = new TupleTwo<>(JdbcType.BIT, BooleanTypeHandler.class);
		jdbcTypeMap.put(Boolean.class, tuple);
		jdbcTypeMap.put(Boolean.TYPE, tuple);

		tuple = new TupleTwo<>(JdbcType.DATE, DateTypeHandler.class);
		jdbcTypeMap.put(Date.class, tuple);

		tuple = new TupleTwo<>(JdbcType.DATE, SqlDateTypeHandler.class);
		jdbcTypeMap.put(java.sql.Date.class, tuple);

		tuple = new TupleTwo<>(JdbcType.DATE, SqlTimestampTypeHandler.class);
		jdbcTypeMap.put(java.sql.Timestamp.class, tuple);

		tuple = new TupleTwo<>(JdbcType.VARCHAR, BigIntegerTypeHandler.class);
		jdbcTypeMap.put(BigInteger.class, tuple);

		tuple = new TupleTwo<>(JdbcType.DECIMAL, BigDecimalTypeHandler.class);
		jdbcTypeMap.put(BigDecimal.class, tuple);

		tuple = new TupleTwo<>(JdbcType.INTEGER, IntegerTypeHandler.class);
		jdbcTypeMap.put(Enum.class, tuple);

		tuple = new TupleTwo<>(JdbcType.TINYINT, ByteArrayTypeHandler.class);
		jdbcTypeMap.put(byte[].class, tuple);
		jdbcTypeMap.put(Byte[].class, tuple);
	}

	/**
	 * 获取属性类型映射
	 * 
	 * @param property
	 *            属性
	 * @return
	 */
	public static TupleTwo<JdbcType, Class<? extends TypeHandler<?>>> getPropertyTypeMap(BeanProperty property) {
		JdbcType jdbcType = null;
		Class<? extends TypeHandler<?>> typeHandlerClass = null;
		TupleTwo<JdbcType, Class<? extends TypeHandler<?>>> columntypeMap = null;
		ColumnType columnType = property.getAnnotation(ColumnType.class);
		if (columnType != null) {
			columntypeMap = new TupleTwo<JdbcType, Class<? extends TypeHandler<?>>>(columnType.jdbcType(),
					columnType.typeHandlerClass());
		}
		if (columntypeMap != null && columntypeMap.getItem1() != null
				&& !columntypeMap.getItem1().equals(JdbcType.UNDEFINED)) {
			jdbcType = columntypeMap.getItem1();
		}
		if (columntypeMap != null && columntypeMap.getItem2() != null
				&& !columntypeMap.getItem2().equals(UnknownTypeHandler.class)) {
			typeHandlerClass = columntypeMap.getItem2();
		}
		if (jdbcType == null || typeHandlerClass == null) {
			TupleTwo<JdbcType, Class<? extends TypeHandler<?>>> typeMap = getDefaultTypeMap(property.getType());
			if (jdbcType == null) {
				jdbcType = typeMap.getItem1();
			}
			if (typeHandlerClass == null) {
				typeHandlerClass = typeMap.getItem2();
			}
		}
		return new TupleTwo<JdbcType, Class<? extends TypeHandler<?>>>(jdbcType, typeHandlerClass);
	}

	/**
	 * 获取 类型 映射
	 * 
	 * @param type
	 *            java类型
	 * @return
	 */
	public static TupleTwo<JdbcType, Class<? extends TypeHandler<?>>> getDefaultTypeMap(Class<?> type) {
		TupleTwo<JdbcType, Class<? extends TypeHandler<?>>> map = jdbcTypeMap.get(type);
		if (map == null) {
			if (Enum.class.isAssignableFrom(type)) {
				map = jdbcTypeMap.get(Enum.class);
			}
			if (map == null) {
				map = new TupleTwo<JdbcType, Class<? extends TypeHandler<?>>>(JdbcType.UNDEFINED,
						UnknownTypeHandler.class);
			}
		}
		return map;
	}

	/**
	 * 获取或默认列
	 * 
	 * @param property
	 *            属性
	 * @param specificationDefinition
	 *            规范
	 * @return
	 */
	public static Column getOrDefaultColumn(BeanProperty property, ISpecificationDefinition specificationDefinition) {
		Column column = property.getAnnotation(Column.class);
		if (column == null) {
			column = new Column() {
				@Override
				public Class<? extends Annotation> annotationType() {
					return Column.class;
				}

				@Override
				public String name() {
					if (specificationDefinition != null) {
						return specificationDefinition.defaultColumnName(property.getName());
					}
					return property.getName();	
				}

				@Override
				public boolean unique() {
					return false;
				}

				@Override
				public boolean nullable() {
					return true;
				}

				@Override
				public boolean insertable() {
					return true;
				}

				@Override
				public boolean updatable() {
					return true;
				}

				@Override
				public String columnDefinition() {
					return null;
				}

				@Override
				public String table() {
					return null;
				}

				@Override
				public int length() {
					return 0;
				}

				@Override
				public int precision() {
					return 0;
				}

				@Override
				public int scale() {
					return 0;
				}
			};
		}
		return column;
	}

	/**
	 * 获取列排序方式
	 * 
	 * @param property
	 *            属性
	 * @return
	 */
	public static String getColumnOrderBy(BeanProperty property) {
		OrderBy orderBy = property.getAnnotation(OrderBy.class);
		if (orderBy != null) {
			return orderBy.value();
		}
		return "";
	}

	/**
	 * 获取列的序列名称
	 * 
	 * @param property
	 *            属性
	 * @return
	 */
	public static String getColumnSequenceName(BeanProperty property) {
		SequenceGenerator generator = property.getAnnotation(SequenceGenerator.class);
		if (generator != null) {
			return generator.sequenceName();
		}
		return "";
	}

	/**
	 * 获取列生成方式
	 * 
	 * @param property
	 *            属性
	 * @return
	 */
	public static GenerationType getColumnGenerationType(BeanProperty property) {
		GeneratedValue gv = property.getAnnotation(GeneratedValue.class);
		if (gv != null && gv.strategy() != null) {
			return gv.strategy();
		}
		return GenerationType.AUTO;
	}
}
